home *** CD-ROM | disk | FTP | other *** search
- /*
- * linux/m68k/traps.c
- *
- * Copyright (C) 1993, 1994 by Hamish Macdonald
- *
- * 68040 fixes by Michael Rausch
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file README.legal in the main directory of this archive
- * for more details.
- */
-
- /*
- * Sets up all exception vectors
- */
-
- #include <asm/system.h>
- #include <asm/segment.h>
- #include <linux/config.h>
- #include <linux/traps.h>
- #include <linux/sched.h>
- #include <linux/signal.h>
- #include <linux/kernel.h>
- #include <linux/mm.h>
- #include <linux/types.h>
- #include <linux/a.out.h>
- #include <linux/user.h>
- #include <linux/string.h>
- #include <linux/bootinfo.h>
-
- /* assembler routines */
- asmlinkage void system_call(void);
- asmlinkage void buserr(void);
- asmlinkage void trap(void);
- asmlinkage void inthandler(void);
- asmlinkage void nmihandler(void);
-
- e_vector vectors[256] = {
- 0, 0, buserr, trap, trap, trap, trap, trap,
- trap, trap, trap, trap, trap, trap, trap, trap,
- trap, trap, trap, trap, trap, trap, trap, trap,
- inthandler, nmihandler, nmihandler, nmihandler,
- inthandler, nmihandler, nmihandler, inthandler,
- /* TRAP #0-15 */
- system_call, trap, trap, trap, trap, trap, trap, trap,
- trap, trap, trap, trap, trap, trap, trap, trap,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- };
-
- /* nmi handler for the Amiga */
- asm(".text
- _nmihandler: rte");
-
- void trap_init (void)
- {
- int i;
-
- /* setup the exception vector table */
- __asm__ volatile ("movec %0,vbr" : : "r" ((void*)vectors));
-
- for (i = 112; i < 256; i++)
- vectors[i] = trap;
-
- /* if running on an amiga, make the NMI interrupt do nothing */
- if (boot_info.machtype == MACH_AMIGA) {
- vectors[VEC_INT7] = nmihandler;
- }
-
- #ifdef CONFIG_FPSP_040
- if (boot_info.cputype & CPU_68040) {
- /* set up FPSP entry points */
- asmlinkage void dz_vec(void) asm ("dz");
- asmlinkage void inex_vec(void) asm ("inex");
- asmlinkage void ovfl_vec(void) asm ("ovfl");
- asmlinkage void unfl_vec(void) asm ("unfl");
- asmlinkage void snan_vec(void) asm ("snan");
- asmlinkage void operr_vec(void) asm ("operr");
- asmlinkage void bsun_vec(void) asm ("bsun");
- asmlinkage void fline_vec(void) asm ("fline");
- asmlinkage void unsupp_vec(void) asm ("unsupp");
-
- vectors[VEC_FPDIVZ] = dz_vec;
- vectors[VEC_FPIR] = inex_vec;
- vectors[VEC_FPOVER] = ovfl_vec;
- vectors[VEC_FPUNDER] = unfl_vec;
- vectors[VEC_FPNAN] = snan_vec;
- vectors[VEC_FPOE] = operr_vec;
- vectors[VEC_FPBRUC] = bsun_vec;
- vectors[VEC_FPBRUC] = bsun_vec;
- vectors[VEC_LINE11] = fline_vec;
- vectors[VEC_FPUNSUP] = unsupp_vec;
- }
- #endif
- }
-
- char *vec_names[] = {
- "RESET SP", "RESET PC", "BUS ERROR", "ADDRESS ERROR",
- "ILLEGAL INSTRUCTION", "ZERO DIVIDE", "CHK", "TRAPcc",
- "PRIVILEGE VIOLATION", "TRACE", "LINE 1010", "LINE 1111",
- "UNASSIGNED RESERVED 12", "COPROCESSOR PROTOCOL VIOLATION",
- "FORMAT ERROR", "UNINITIALIZED INTERRUPT",
- "UNASSIGNED RESERVED 16", "UNASSIGNED RESERVED 17",
- "UNASSIGNED RESERVED 18", "UNASSIGNED RESERVED 19",
- "UNASSIGNED RESERVED 20", "UNASSIGNED RESERVED 21",
- "UNASSIGNED RESERVED 22", "UNASSIGNED RESERVED 23",
- "SPURIOUS INTERRUPT", "LEVEL 1 INT", "LEVEL 2 INT", "LEVEL 3 INT",
- "LEVEL 4 INT", "LEVEL 5 INT", "LEVEL 6 INT", "LEVEL 7 INT",
- "SYSCALL", "TRAP #1", "TRAP #2", "TRAP #3",
- "TRAP #4", "TRAP #5", "TRAP #6", "TRAP #7",
- "TRAP #8", "TRAP #9", "TRAP #10", "TRAP #11",
- "TRAP #12", "TRAP #13", "TRAP #14", "TRAP #15"
- };
-
- char *space_names[] = {
- "Space 0", "User Data", "User Program", "Space 3",
- "Space 4", "Super Data", "Super Program", "CPU"
- };
-
-
-
- extern void die_if_kernel(char *,struct frame *,int);
- asmlinkage void do_page_fault(struct frame *fp, unsigned long address,
- unsigned long error_code);
-
- asmlinkage void trap_c(struct frame *fp);
-
- static unsigned long probe040 (int iswrite, int fc, unsigned long addr)
- {
- unsigned long mmusr;
- unsigned short fs = get_fs();
-
- set_fs (fc);
-
- if (iswrite)
- /* write */
- asm volatile ("movel %1,a0\n\t"
- ".word 0xf548\n\t" /* ptestw (a0) */
- ".long 0x4e7a8805\n\t" /* movec mmusr,a0 */
- "movel a0,%0"
- : "=g" (mmusr)
- : "g" (addr)
- : "a0");
- else
- asm volatile ("movel %1,a0\n\t"
- ".word 0xf568\n\t" /* ptestr (a0) */
- ".long 0x4e7a8805\n\t" /* movec mmusr,a0 */
- "movel a0,%0"
- : "=g" (mmusr)
- : "g" (addr)
- : "a0");
-
-
- set_fs (fs);
-
- return mmusr;
- }
-
- static void do_040writeback (unsigned short itsnotme,
- unsigned short ssw,
- unsigned short wbs,
- unsigned long wba,
- unsigned long wbd,
- struct frame *fp)
- {
- unsigned short fs = get_fs ();
- unsigned long mmusr;
- unsigned long wba_2ndpage;
-
-
- if(itsnotme)
- {
- /*
- * to avoid another page fault, check the address we're going to,
- * and map it in
- */
- mmusr = probe040 (1, wbs & WBTM_040, wba);
- if (mmusr & MMU_R_040)
- do_page_fault (fp, wba, 1);
- else
- do_page_fault (fp, wba, 0);
- }
-
- /*
- * if the write spans a page, make sure that the extra page
- * is mapped in.
- * MR: information concerning this may be retrieved from the SSW;
- * thus we can save us the page comparison
- */
- if(ssw & MA_040)
- {
- /* Added missing parentheses (MA) */
- wba_2ndpage = wba + (((wbs & WBSIZ_040)==BA_SIZE_WORD)?1:3);
- mmusr = probe040 (1, wbs & WBTM_040, wba_2ndpage);
- if (mmusr & MMU_R_040)
- do_page_fault (fp, wba_2ndpage, 1);
- else
- do_page_fault (fp, wba_2ndpage, 0);
- }
-
- set_fs (wbs & WBTM_040);
- switch (wbs & WBSIZ_040) {
- case BA_SIZE_BYTE:
- put_user_byte (wbd & 0xff, (char *)wba);
- break;
- case BA_SIZE_WORD:
- put_user_word (wbd & 0xffff, (short *)wba);
- break;
- case BA_SIZE_LONG:
- put_user_long (wbd, (int *)wba);
- break;
- }
- set_fs (fs);
- }
-
- static inline void access_error040 (struct frame *fp)
- {
- unsigned short ssw = fp->un.fmt7.ssw;
- unsigned long mmusr;
-
- #ifdef DEBUG
- printk("ssw=%#x, fa=%#lx\n", ssw, fp->un.fmt7.faddr);
- #endif
-
- if (ssw & ATC_040) {
- unsigned long addr = fp->un.fmt7.faddr;
-
- /* MMU error, get the MMUSR info for this access */
- mmusr = probe040 (!(ssw & RW_040), ssw & TM_040, addr);
-
- #ifdef DEBUG
- printk("mmusr = %lx\n", mmusr);
- #endif
-
- if (mmusr & MMU_R_040)
- do_page_fault (fp, addr, 1); /* we hit a resident page, so we want to write to it ?! */
- else
- do_page_fault (fp, addr, 0); /* not resident */
-
- } else {
- printk ("68040 access error, ssw=%x\n", ssw);
- trap_c (fp);
- }
-
- #if 0
- if (fp->un.fmt7.wb1s & WBV_040)
- printk("access_error040: cannot handle 1st writeback. oops.\n");
- #endif
-
- /*
- * We may have to do a couple of writebacks here.
- *
- * MR: we can speed up the thing a little bit and let do_040writeback()
- * not produce another page fault as wb2 corresponds to the address that
- * caused the fault. on write faults no second fault is generated, but
- * on read faults for security reasons (although per definitionem impossible)
- */
-
- if (fp->un.fmt7.wb2s & WBV_040 && (fp->un.fmt7.wb2s & WBTT_040) != BA_TT_MOVE16)
- do_040writeback (!(ssw & RW_040), ssw,
- fp->un.fmt7.wb2s, fp->un.fmt7.wb2a, fp->un.fmt7.wb2d, fp);
-
- if (fp->un.fmt7.wb3s & WBV_040)
- do_040writeback (1, ssw,
- fp->un.fmt7.wb3s, fp->un.fmt7.wb3a, fp->un.fmt7.wb3d, fp);
- }
-
- static inline void bus_error030 (struct frame *fp)
- {
- volatile unsigned short temp;
- unsigned short mmusr;
- unsigned long addr, desc;
- unsigned short ssw = fp->un.fmtb.ssw;
-
- #ifdef DEBUG
- printk ("SSW=%#06x ", ssw);
-
- if (ssw & (FC | FB))
- printk ("Instruction fault at %#010x\n",
- ssw & FC ?
- fp->format == 0xa ? fp->pc + 2 : fp->un.fmtb.baddr - 2
- :
- fp->format == 0xa ? fp->pc + 4 : fp->un.fmtb.baddr);
- if (ssw & DF)
- printk ("Data %s fault at %#010x in %s (pc=%#lx)\n",
- ssw & RW ? "read" : "write",
- fp->un.fmtb.daddr,
- space_names[ssw & DFC], fp->pc);
- #endif
-
- if (fp->sr & PS_S) {
- /* kernel fault must be a data fault to user space */
- if (! ((ssw & DF) && ((ssw & DFC) == USER_DATA))) {
- /* instruction fault or kernel data fault! */
- if (ssw & (FC | FB))
- printk ("Instruction fault at %#010lx\n",
- fp->pc);
- if (ssw & DF) {
- printk ("Data %s fault at %#010lx in %s (pc=%#lx)\n",
- ssw & RW ? "read" : "write",
- fp->un.fmtb.daddr,
- space_names[ssw & DFC], fp->pc);
- }
- printk ("BAD KERNEL BUSERR");
- trap_c (fp);
- }
- } else {
- /* user fault */
- if (!(ssw & (FC | FB)) && !(ssw & DF))
- /* not an instruction fault or data fault! BAD */
- panic ("USER BUSERR w/o instruction or data fault");
- }
-
- /* get the fault address */
- if (fp->format == 0xA )
- if (ssw & FC)
- addr = fp->pc + 2;
- else if (ssw & FB)
- addr = fp->pc + 4;
- else /* data fault */
- addr = fp->un.fmta.daddr;
- else
- if (ssw & FC)
- addr = fp->un.fmtb.baddr - 2;
- else if (ssw & FB)
- addr = fp->un.fmtb.baddr;
- else /* data fault */
- addr = fp->un.fmtb.daddr;
-
- asm volatile ("ptestr #1,%2@,#7,%0\n\t"
- "pmove psr,%1@"
- : "=a&" (desc)
- : "a" (&temp), "a" (addr));
- mmusr = temp;
-
- #ifdef DEBUG
- printk ("mmusr is %#x for addr %#lx in task %p\n",
- mmusr, addr, current);
- printk ("descriptor address is %#lx, contents %#lx\n",
- PTOV(desc), *(unsigned long *)PTOV(desc));
- #endif
-
- if (mmusr & MMU_I)
- do_page_fault (fp, addr, 0);
- else if ((mmusr & MMU_WP) && !(ssw & RW))
- do_page_fault (fp, addr, 1);
- else if (mmusr & (MMU_B|MMU_L|MMU_S)) {
- printk ("invalid %s access at %#lx from pc %#lx\n",
- !(ssw & RW) ? "write" : "read", addr, fp->pc);
- die_if_kernel("Oops",fp,mmusr);
- send_sig(SIGSEGV, current, 1);
- return;
- } else {
- #ifdef DEBUG
- static volatile long tlong;
- #endif
-
- printk ("wierd %s access at %#lx from pc %#lx (ssw is %#x)\n",
- !(ssw & RW) ? "write" : "read", addr, fp->pc, ssw);
- asm volatile ( /* "ptestr #1,%1@,#0\n\t" */
- "pmove psr,%0@"
- :
- : "a" (&temp), "a" (addr));
- mmusr = temp;
-
- printk ("level 0 mmusr is %#x\n", mmusr);
- #if defined(DEBUG) && 0
- asm volatile ("pmove tt0,%0@"
- : /* no outputs */
- : "a" (&tlong));
- printk ("tt0 is %#lx, ", tlong);
- asm volatile ("pmove tt1,%0@"
- : /* no outputs */
- : "a" (&tlong));
- printk ("tt1 is %#lx\n", tlong);
-
- #endif
- die_if_kernel("Oops",fp,mmusr);
- send_sig(SIGSEGV, current, 1);
- return;
- }
-
- /* setup an ATC entry for the access about to be retried */
- if (!(ssw & RW))
- asm volatile ("ploadw #1,%0@" : /* no outputs */
- : "a" (addr));
- else
- asm volatile ("ploadr #1,%0@" : /* no outputs */
- : "a" (addr));
- }
-
- asmlinkage void buserr_c(struct frame *fp)
- {
-
- #ifdef DEBUG
- printk ("*** Bus Error *** Format is %x\n", fp->format);
- #endif
-
- switch (fp->format) {
- case 7: /* 68040 access error */
- access_error040 (fp);
- break;
- case 0xa:
- case 0xb:
- bus_error030 (fp);
- break;
- default:
- panic ("bad frame format %d", fp->format);
- }
- }
-
-
- void bad_super_trap (struct frame *fp)
- {
- unsigned long isp;
-
- /* fetch interrupt stack pointer */
- __asm__ __volatile__ ("movec isp,%0" : "=g" (isp));
-
- if (fp->vector < 48*4)
- printk ("*** %s *** FORMAT=%X\n",
- vec_names[fp->vector >> 2], fp->format);
- else
- printk ("*** Exception %d *** FORMAT=%X\n",
- fp->vector >> 2, fp->format);
-
- printk ("PC=%#010lx SR=%#06x SP=%#010lx ISP=%#010lx\n",
- fp->pc, fp->sr, (ulong)fp, isp);
- printk ("D0=%#010lx D1=%#010lx D2=%#010lx D3=%#010lx\n",
- fp->d0, fp->regs[0], fp->regs[1], fp->regs[2]);
- printk ("D4=%#010lx D5=%#010lx D6=%#010lx D7=%#010lx\n",
- fp->regs[3], fp->regs[4], fp->regs[5], fp->regs[6]);
- printk ("A0=%#010lx A1=%#010lx A2=%#010lx A3=%#010lx\n",
- fp->regs[7], fp->regs[8], fp->regs[9], fp->regs[10]);
- printk ("A4=%#010lx A5=%#010lx A6=%#010lx USP=%#010lx\n",
- fp->regs[11], fp->regs[12], fp->regs[13], fp->usp);
-
- if ((fp->vector >> 2) == VEC_ADDRERR
- && !(boot_info.cputype & CPU_68040)) {
- unsigned short ssw = fp->un.fmtb.ssw;
-
- printk ("SSW=%#06x ", ssw);
-
- if (ssw & RC)
- printk ("Pipe stage C instruction fault at %#010lx\n",
- fp->format == 0xA ? fp->pc + 2 :
- fp->un.fmtb.baddr - 2);
- if (ssw & RB)
- printk ("Pipe stage B instruction fault at %#010lx\n",
- fp->format == 0xA ? fp->pc + 4 :
- fp->un.fmtb.baddr);
- if (ssw & DF)
- printk ("Data %s fault at %#010lx in %s (pc=%#lx)\n",
- ssw & RW ? "read" : "write",
- fp->un.fmtb.daddr, space_names[ssw & DFC],
- fp->pc);
-
- printk ("Word at pc is %#x\n", *(ushort *)fp->pc);
- }
- panic ("BAD KERNEL TRAP");
- }
-
- asmlinkage void trap_c(struct frame *fp)
- {
- int sig;
-
- if ((fp->sr & PS_S) && (fp->vector >> 2) == VEC_TRACE
- && !(fp->sr & PS_T)) {
- /* traced a trapping instruction */
- unsigned char *lp = ((unsigned char *)&fp->un.fmt2) + 4;
- current->flags |= PF_DTRACE;
- /* clear the trace bit */
- (*(unsigned short *)lp) &= ~PS_T;
- return;
- } else if (fp->sr & PS_S)
- bad_super_trap(fp);
-
- /* send the appropriate signal to the user program */
- switch (fp->vector >> 2) {
- case VEC_BUSERR:
- case VEC_ADDRERR:
- sig = SIGSEGV;
- break;
- case VEC_ILLEGAL:
- case VEC_PRIV:
- case VEC_LINE10:
- case VEC_LINE11:
- case VEC_COPROC:
- case VEC_TRAP1:
- case VEC_TRAP2:
- case VEC_TRAP3:
- case VEC_TRAP4:
- case VEC_TRAP5:
- case VEC_TRAP6:
- case VEC_TRAP7:
- case VEC_TRAP8:
- case VEC_TRAP9:
- case VEC_TRAP10:
- case VEC_TRAP11:
- case VEC_TRAP12:
- case VEC_TRAP13:
- case VEC_TRAP14:
- sig = SIGILL;
- break;
- case VEC_ZERODIV:
- case VEC_FPBRUC:
- case VEC_FPIR:
- case VEC_FPDIVZ:
- case VEC_FPUNDER:
- case VEC_FPOE:
- case VEC_FPOVER:
- case VEC_FPNAN:
- sig = SIGFPE;
- break;
- case VEC_TRACE: /* ptrace single step */
- fp->sr &= ~PS_T;
- case VEC_TRAP15: /* breakpoint */
- sig = SIGTRAP;
- break;
- default:
- sig = SIGILL;
- break;
- }
-
- send_sig (sig, current, 1);
- }
-
- void die_if_kernel (char *str, struct frame *fp, int nr)
- {
- u_char *ssp = (u_char *)fp;
- int i;
-
- if (!(fp->sr & PS_S))
- return;
-
- printk("%s: %04x\n",str,nr&0xffff);
- printk("PC: %08lx\nSR: %04x\n", fp->pc,fp->sr);
- printk("Pid: %d\n",current->pid);
- for (i = 0; i < 10; i++)
- printk ("%02x ", 0xff & ssp[i]);
- printk ("\n");
- do_exit(SIGSEGV);
- }
-
- void arch_core_dump (struct user *up, long signr, struct pt_regs *regs)
- {
- up->magic = CMAGIC;
- up->start_code = 0;
- up->start_stack = regs->usp & ~(PAGE_SIZE - 1);
- up->u_tsize = ((unsigned long) current->end_code) >> 12;
- up->u_dsize = ((unsigned long) (current->brk + (PAGE_SIZE-1))) >> 12;
- up->u_dsize -= up->u_tsize;
- up->u_ssize = 0;
- if (up->start_stack < TASK_SIZE)
- up->u_ssize = ((unsigned long) (TASK_SIZE - up->start_stack)) >> 12;
- /* If the size of the dump file exceeds the rlimit, then see what would happen
- if we wrote the stack, but not the data area. */
- if ((up->u_dsize+up->u_ssize+1) * PAGE_SIZE >
- current->rlim[RLIMIT_CORE].rlim_cur)
- up->u_dsize = 0;
- /* Make sure we have enough room to write the stack and data areas. */
- if ((up->u_ssize+1) * PAGE_SIZE >
- current->rlim[RLIMIT_CORE].rlim_cur)
- up->u_ssize = 0;
- strncpy(up->u_comm, current->comm, sizeof(current->comm));
- up->u_ar0 = (struct pt_regs *)(((int)(&up->regs)) -((int)(up)));
- up->signal = signr;
- up->regs = *regs;
- /* floating point stuff valid */
- up->u_fpvalid = 1;
- /* save floating point registers */
- asm volatile ("fmovem fpcr/fpsr,%0@"
- :: "a" (up->m68kfp.fpcntl));
- asm volatile ("fmovemx fp0-fp7,%0@"
- :: "a" (up->m68kfp.fpregs)
- : "memory");
- }
-